﻿using System;

using System.Collections.Generic;
using System.Text;
using Silvermoon.Controls;
using System.Threading;
using Silvermoon.Controls.Classes;
using Silvermoon.Controls.Interfaces;
using System.Drawing;
using Silvermoon.OpenGL;
using Silvermoon.Animations;
using System.IO;
using System.Reflection;
using Silvermoon.Visuals;
using Silvermoon.Controls.Base;

namespace FileExplorer
{
    /// <summary>
    /// The ExplorerWindow contains a TreeView to browse trough the folder structure of the device.
    /// The Root Folder "My Device" is created normally by adding an item for each folder/file to the 
    /// TreeView's Items collection, while all other folders are populated on demand in a virtual mode.
    /// </summary>
    public class ExplorerWindow : Window
    {
        private ImageToolbar dlgToolbar;
        private ImageToolbar toolbar;
        private TreeView treeView;
        private ContextMenu menu;
        private ContextMenu clipboardMenu;
        private MenuItem sortByItem;
        private Panel editPrefsPanel;
        private PageDescriptor editPrefs;
        private IPage lastPage;

        public ExplorerWindow()
            : base()
        {
            Color = Color.White;
            CreateTreeView();
            BuildMenu();
            BuildClipboardMenu();
            toolbar = new ImageToolbar { ButtonSize = new Size(64, 64), Color = Color.Black };
            dlgToolbar = new ImageToolbar { ButtonSize = new Size(64, 64), Color = Color.DarkRed };
            this.Toolbar = toolbar;
            InputBgColor = Color.SlateGray;

            SetButtons(true);

            // we want to populate the root folder after the window has become visible:
            Screen.Loaded += new EventHandler(OnScreenLoaded);
            Flags = ControlFlags.IgnoreChildClipping | ControlFlags.IgnoreClipping;
            Invalidate(InvalidationFlag.LayoutAppearance);
        }

        public override void Dispose()
        {
            menu.Dispose();
            base.Dispose();
        }


        private void CreatePageTransition()
        {
            PageContainer page = new PageContainer { Margin = new Margin(0) };
            Controls.Add(page);
        }


        /// <summary>
        /// Builds the menu for the right toolbar button.
        /// </summary>
        private void BuildMenu()
        {
            menu = new ContextMenu { Width = 320, MenuPosition = MenuPosition.TopRight };
            menu.BeginInit();
            menu.AddItem("Refresh").Click += (s, e) => Refresh();
            MenuItem item = menu.AddItem("Sort by");
            {
                item.AddItem("Name", true).Click += OnSortItemClick;
                item.AddItem("Date Modified", false).Click += OnSortItemClick;
                item.AddItem("Size", false).Click += OnSortItemClick;
                item.AddSeparator();
                item.AddItem("Descending", false).Click += (s, e) => ((MenuItem)s).Checked ^= true;
            }
            sortByItem = item;
            menu.AddSeparator();
            item = menu.AddItem("Edit");
            {
                item.AddItem("Cut");
                item.AddItem("Copy");
                item.AddItem("Paste");
            }
            menu.AddItem("Edit Mode").Click += (s, e) => FileItemMode.Modes.EditMode ^= true;
            menu.AddSeparator();
            menu.AddItem("Preferences...").Click += (s, e) => EditPreferences();
            menu.AddSeparator();
            menu.AddItem("Exit").Click += (s, e) => { Color = Color.White; Hide(TransitionMode.ZoomOut | TransitionMode.Fade); };

            menu.EndInit();
        }

        private void BuildClipboardMenu()
        {
            clipboardMenu = new ContextMenu { Width = 320, MenuPosition = MenuPosition.TopRight };
            clipboardMenu.BeginInit();
            clipboardMenu.AddItem("Cut");
            clipboardMenu.AddItem("Copy");
            clipboardMenu.AddItem("Paste");
            clipboardMenu.EndInit();
        }

        static readonly string[] imageResources = new string[]
        {
            "Demo.Images.Back.png",
            "Demo.Images.Home.png",
            "Demo.Images.Keyboard.png",
            "Demo.Images.Faves.png",
            "Demo.Images.Menu.png"
        };


        /// <summary>
        /// Sets the buttons in the window's toolbar.
        /// </summary>
        private void SetButtons(bool setHandler)
        {
            int idx = 0;
            foreach (ImageButton b in toolbar.Buttons)
            {
                Stream s = Assembly.GetCallingAssembly().GetManifestResourceStream(imageResources[idx++]);
                Texture te = new Texture();
                te.LoadFromStream(s);

                b.Image = te;
                b.Color = Color.White;
                b.PressedColor = Color.FromArgb(20, 172, 255);
            }
            if (setHandler)
            {
                toolbar.Buttons[0].Click -= OnLeftButtonClick;
                toolbar.Buttons[0].Click += OnLeftButtonClick;
            }
            toolbar.Buttons[0].Enabled = treeView.Page.ParentPage != null;
            toolbar.Buttons[2].DownChanged += (sender, e) => toolbar.Window.ShowKeyboard = ((ButtonBase)sender).IsDown;
            toolbar.Buttons[2].Toggle = true;
            toolbar.Buttons[3].Enabled = true;
            toolbar.Buttons[1].Click += (sender, e) => { Color = Color.White; TransitionDuration = 400; Hide(TransitionMode.ZoomOut); TransitionDuration = 350; };

            dlgToolbar.Buttons[0].Image = toolbar.Buttons[0].Image;
            dlgToolbar.Buttons[0].Click += (s, e) => GoBackToNode();

            toolbar.SetMenu(4, menu);
            toolbar.SetMenu(3, clipboardMenu);
        }

        void OnLeftButtonClick(object sender, EventArgs e)
        {
            treeView.GoBack();
        }


        private void CreateTreeView()
        {
            treeView = new TreeView { Margin = new Margin(0) };
            treeView.ItemHeight = FileItem.ItemHeight;
            treeView.Header.Color = Color.Black;
            treeView.Title = "My Device";
            treeView.PageLoaded += new EventHandler<PageEventArgs>(OnPageLoaded);
            treeView.PageLoading += new EventHandler<PageEventArgs>(OnPageLoading);
            treeView.Populate += new EventHandler<TreeViewEventArgs>(OnPopulate);
            treeView.AttachItem += new EventHandler<TreeViewItemEventArgs>(OnAttachItem);

            treeView.Items.Add(new WaitItem());
            Controls.Add(treeView);

            // Notify the only instance of FileItemTransition where it can find the TreeView:
            FileItemMode.Modes.TreeView = treeView;
        }


        /// <summary>
        /// when one of the sort by subitems changes it's checked state, we mutually disable the checked state of the others:
        /// </summary>
        private void OnSortItemClick(object sender, EventArgs e)
        {
            MenuItem selected = (MenuItem)sender;
            for (int i = 0; i < 3; i++)
            {
                MenuItem item = sortByItem.Items[i] as MenuItem;
                item.Checked = selected == item;
            }
            int index = sortByItem.Items.IndexOf(selected);
            SortFolder(index);
        }

        /// <summary>
        /// Sort the folder.
        /// </summary>
        /// <param name="sortMode">Specifies how to sort the folder.</param>
        private void SortFolder(int sortMode)
        {

        }

        /// <summary>
        /// This occurs when the TreeView accesses a node that is not yet available and must be created or attached to the TreeView.
        /// </summary>
        void OnAttachItem(object sender, TreeViewItemEventArgs e)
        {
            if (e.ParentItem != null)
            {
                // The request must comefrom a FileItem:
                FileItem parent = (FileItem)e.ParentItem;
                int index = e.Index;

                // Get the prefetched data from the FileItem for which a child node needs to be created:
                string title = parent.Files[index];
                if (title.StartsWith("?"))
                {
                    // if it is a header (marked with a "?"), then add a ItemHeader:
                    e.Item = new ItemHeader { Text = title.Remove(0, 1), Color = Color.Silver, TextColor = Color.DimGray };
                }
                else
                {
                    // otherwise it is a file or folder for which we create a new FileItem node:
                    e.Item = new FileItem(title, parent.FileOffset > index);
                }
            }
        }

        /// <summary>
        /// This occurs when the treeview is navigated to another node in a different hierarchy.
        /// If the node has not yet Items, they can be populated here:
        /// </summary>
        void OnPopulate(object sender, TreeViewEventArgs e)
        {
            FileItem item = (FileItem)e.Node;

            // if the item is already populated or it is  not a folder, we do nothing:
            if (item.Populated || !item.IsFolder) return;

            PopulateFolder(item);

            // TreeViewEventArgs requires to return the number of nodes to set, so we do this here:
            e.NodeCount = item.Files.Count;
        }

        /// <summary>
        /// Populates the child nodes of a FileItem in a separate thread in background.
        /// </summary>
        private void PopulateFolder(FileItem item)
        {
            Screen.IsBusy = true;
            ManualResetEvent resetEvent = new ManualResetEvent(false);
            ThreadPriority priority = Thread.CurrentThread.Priority;
            try
            {
                Thread t = new Thread(() => { item.Populate(); resetEvent.Set(); });
                t.Priority = ThreadPriority.Normal;
                Thread.CurrentThread.Priority = ThreadPriority.Normal;
                t.Start();

                // while the folder is populated in a spearate thread,
                // update the busy pointer in intervals of 40ms:
                while (!resetEvent.WaitOne(40, false)) Screen.Progess();
                t.Join();
            }
            finally
            {
                resetEvent.Close();
                Thread.CurrentThread.Priority = priority;
            }
            Screen.IsBusy = false;

            //mark the item to be populated so we don't need to do this again:
            item.Populated = true;
        }

        /// <summary>
        /// This occurs before the treeview is changing to view another node.
        /// Here we can for instance preload data to show when the new node is transitioning.
        /// </summary>
        void OnPageLoading(object sender, PageEventArgs e)
        {
            // enable the back button if the new page has a parent page:
            toolbar.Buttons[0].Enabled = e.NewPage.ParentPage != null || e.NewPage == this.editPrefs;

            FileItemMode.Modes.EditMode = false;
        }

        /// <summary>
        /// This occurs after the treeview has changed to view another node and completed transition to the new node.
        /// We can use this event to cleanup data from the previous node to save memory if we want to,
        /// and we can initialize other controls. 
        /// </summary>
        void OnPageLoaded(object sender, PageEventArgs e)
        {
            CleanupPreviousPage(e);
        }

        private void CleanupPreviousPage(PageEventArgs e)
        {
        }

        private bool shown = false;

        /// <summary>
        /// now that the form is visible, we are populating the root folder:
        /// </summary>
        protected override void OnShown()
        {
            base.OnShown();
            if (!shown)
            {
                shown = true;
                FileUtil.Populate(treeView, @"\");
            }
        }

        /// <summary>
        /// This method is called once after the screen has rendered it's first scene.
        /// We use this event to Populate the root node structure, while the window is already visible.
        /// </summary>
        public void OnScreenLoaded(object sender, EventArgs e)
        {
            Invalidate(InvalidationFlag.Size);
            Screen.Loaded -= OnScreenLoaded;
            shown = true;
            FileUtil.Populate(treeView, @"\");
        }

        /// <summary>
        /// Here we make a transition to the preferences. This is possible, since the TreeView is also a PageContainer.
        /// First we need an IPage that describes the page. We can use a PageDescriptor at this place.
        /// Since the descriptor has no ParentPage set, the transition uses an alpha blend animation to this page.
        /// </summary>
        private void EditPreferences()
        {
            if (editPrefsPanel == null)
            {
                editPrefsPanel = new Panel { TextColor = Color.Black };
                Panel p = editPrefsPanel;
                Button okButton = new Button { Size = new Size(80, 64), Text = "OK", Color = Color.DarkRed, Style= RenderStyle.Chrome };
                okButton.Click += (s, e) => { GoBackToNode(); };

                p.Controls.Add(new Label { Bounds = new Rectangle(12, 12, 120, 64), Text = "Sort by", Alignment = Alignment.Far });
                DropDownButton dropDown = new DropDownButton { Bounds = new Rectangle(12 + 130, 12, 260, 64), Text = "Name", Color = Color.Snow, TextColor = Color.Black };
                dropDown.AddItems("Name", "Date Modified", "Size");
                p.Controls.Add(dropDown);

                p.Controls.Add(new CheckBox { Bounds = new Rectangle(12 + 130, 12 + 64, 200, 48), Text = "Ascending", Checked = true });

                p.Controls.Add(new CheckBox { Bounds = new Rectangle(12 + 130, 12 + 96 + 48 * 1, 320, 48), Text = "Show Hidden Files", Checked = true });
                p.Controls.Add(new CheckBox { Bounds = new Rectangle(12 + 130, 12 + 96 + 48 * 2, 320, 48), Text = "Show System Files", Checked = true });

                // create a PageDescriptor to describe what we want to see in the treeview, which is also a PageContainer:
                editPrefs = new PageDescriptor { Control = p, Title = "Preferences", ParentPage = null, Button = okButton };
            }

            // change the color of the header and to have a fluent transition, preload:
            treeView.Header.Color = Color.DarkRed;
            treeView.Header.PreLoad();


            // Change the toolbar:
            Toolbar = dlgToolbar;



            // remember from where we came from:
            lastPage = treeView.Page;

            // now show the preferences panel:
            treeView.Page = editPrefs;

        }

        void GoBackToNode()
        {
            // Set the default toolbar:
            Toolbar = toolbar;
            treeView.Header.Color = Color.Black;
            treeView.Header.PreLoad();
            treeView.Page = lastPage;
        }

        public void Refresh()
        {
        }

        internal void FocusTreeview()
        {
            treeView.Focus();
        }

        protected override void OnShowKeyboardChanged(bool value)
        {
            if (toolbar != null) toolbar.Buttons[2].IsDown = ShowKeyboard;
            base.OnShowKeyboardChanged(value);
        }

        protected override void OnLayout()
        {
            base.OnLayout();
        }
    }
}
